import 'dart:math';

void main() {
  // 基本类型转换和断言
  var anObject = 'example';
  var tea = 'green tea';
  print(anObject);
  print('I drink $tea.');

  assert(int.parse('42') == 42);
  assert(int.parse('0x42') == 66);
  assert(double.parse('0.50') == 0.5);

  assert(num.parse('42') is int);
  assert(num.parse('0x42') is int);
  assert(num.parse('0.50') is double);

  assert(int.parse('42', radix: 16) == 66);
  assert(42.toString() == '42');
  assert(123.456.toString() == '123.456');
  assert(123.456.toStringAsFixed(2) == '123.46');
  assert(123.456.toStringAsPrecision(2) == '1.2e+2');
  assert(double.parse('1.2e+2') == 120.0);

  assert('Never odd or even'.contains('odd'));
  assert('Never odd or even'.startsWith('Never'));
  assert('Never odd or even'.endsWith('even'));
  assert('Never odd or even'.indexOf('odd') == 6);
  assert('Never odd or even'.substring(6, 9) == 'odd');

  var parts = 'progressive web apps'.split(' ');
  assert(parts.length == 3);
  assert(parts[0] == 'progressive');
  assert('Never odd or even'[0] == 'N');

  for (final char in 'hello'.split('')) {
    print(char);
  }

  var codeUnitList = 'Never odd or even'.codeUnits.toList();
  assert(codeUnitList[0] == 78);
  assert('web apps'.toUpperCase() == 'WEB APPS');
  assert('WEB APPS'.toLowerCase() == 'web apps');
  assert('  hello  '.trim() == 'hello');
  assert(''.isEmpty);
  assert('  '.isNotEmpty);

  var greetingTemplate = 'Hello, NAME!';
  var greeting = greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');
  assert(greeting != greetingTemplate);

  var sb = StringBuffer();
  sb
    ..write('Use a StringBuffer for ')
    ..writeAll(['efficient', 'string', 'creation'], ' ')
    ..write('.');
  var fullString = sb.toString();
  assert(fullString == 'Use a StringBuffer for efficient string creation.');

  var digitSequence = RegExp(r'\d+');
  var lettersOnly = 'llamas live fifteen to twenty years';
  var someDigits = 'llamas live 15 to 20 years';
  assert(!lettersOnly.contains(digitSequence));
  assert(someDigits.contains(digitSequence));
  var exedOut = someDigits.replaceAll(digitSequence, 'XX');
  assert(exedOut == 'llamas live XX to XX years');
  assert(digitSequence.hasMatch(someDigits));
  for (final match in digitSequence.allMatches(someDigits)) {
    print(match.group(0)); // 15, then 20
  }

  // List 示例
  var grains = <String>[];
  assert(grains.isEmpty);
  var fruits = ['apples', 'oranges'];
  fruits.add('kiwis');
  fruits.addAll(['grapes', 'bananas']);
  assert(fruits.length == 5);
  var appleIndex = fruits.indexOf('apples');
  fruits.removeAt(appleIndex);
  assert(fruits.length == 4);
  fruits.clear();
  assert(fruits.isEmpty);
  var vegetables = List.filled(99, 'broccoli');
  assert(vegetables.every((v) => v == 'broccoli'));
  var fruits2 = ['apples', 'oranges'];
  assert(fruits2[0] == 'apples');
  assert(fruits2.indexOf('apples') == 0);
  var fruits3 = ['bananas', 'apples', 'oranges'];
  fruits3.sort((a, b) => a.compareTo(b));
  assert(fruits3[0] == 'apples');
  var fruits4 = <String>[];
  fruits4.add('apples');
  var fruit = fruits4[0];
  assert(fruit is String);

  // Set 示例
  var ingredients = <String>{};
  ingredients.addAll(['gold', 'titanium', 'xenon']);
  assert(ingredients.length == 3);
  ingredients.add('gold');
  assert(ingredients.length == 3);
  ingredients.remove('gold');
  assert(ingredients.length == 2);
  var atomicNumbers = Set.from([79, 22, 54]);
  var ingredients2 = Set<String>();
  ingredients2.addAll(['gold', 'titanium', 'xenon']);
  assert(ingredients2.contains('titanium'));
  assert(ingredients2.containsAll(['titanium', 'xenon']));
  var nobleGases = Set.from(['xenon', 'argon']);
  var intersection = ingredients2.intersection(nobleGases);
  assert(intersection.length == 1);
  assert(intersection.contains('xenon'));

  // Map 示例
  var hawaiianBeaches = {
    'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
    'Big Island': ['Wailea Bay', 'Pololu Beach'],
    'Kauai': ['Hanalei', 'Poipu'],
  };
  var searchTerms = Map();
  var nobleGasesMap = Map<int, String>();
  var nobleGases2 = {54: 'xenon'};
  assert(nobleGases2[54] == 'xenon');
  assert(nobleGases2.containsKey(54));
  nobleGases2.remove(54);
  assert(!nobleGases2.containsKey(54));
  var keys = hawaiianBeaches.keys;
  assert(keys.length == 3);
  assert(Set.from(keys).contains('Oahu'));
  var values = hawaiianBeaches.values;
  assert(values.length == 3);
  assert(values.any((v) => v.contains('Waikiki')));
  assert(hawaiianBeaches.containsKey('Oahu'));
  assert(!hawaiianBeaches.containsKey('Florida'));
  // var teamAssignments = <String, String>{};
  // teamAssignments.putIfAbsent('Catcher', () => pickToughestKid()); // 需自定义 pickToughestKid
  var coffees = <String>[];
  var teas = ['green', 'black', 'chamomile', 'earl grey'];
  assert(coffees.isEmpty);
  assert(teas.isNotEmpty);
  teas.forEach((tea) => print('I drink $tea'));
  hawaiianBeaches.forEach((k, v) {
    print('I want to visit $k and swim at $v');
  });
  var loudTeas = teas.map((tea) => tea.toUpperCase());
  loudTeas.forEach(print);
  var loudTeasList = teas.map((tea) => tea.toUpperCase()).toList();
  bool isDecaffeinated(String teaName) => teaName == 'chamomile';
  var decaffeinatedTeas = teas.where((tea) => isDecaffeinated(tea));
  assert(teas.any(isDecaffeinated));
  assert(!teas.every(isDecaffeinated));

  // Uri 示例
  var uri = 'https://example.org/api?foo=some message';
  var encoded = Uri.encodeFull(uri);
  assert(encoded == 'https://example.org/api?foo=some%20message');
  var decoded = Uri.decodeFull(encoded);
  assert(uri == decoded);
  var encoded2 = Uri.encodeComponent(uri);
  assert(
    encoded2 == 'https%3A%2F%2Fexample.org%2Fapi%3Ffoo%3Dsome%20message',
  );
  var decoded2 = Uri.decodeComponent(encoded2);
  assert(uri == decoded2);
  var uriObj = Uri.parse('https://example.org:8080/foo/bar#frag');
  assert(uriObj.scheme == 'https');
  assert(uriObj.host == 'example.org');
  assert(uriObj.path == '/foo/bar');
  assert(uriObj.fragment == 'frag');
  assert(uriObj.origin == 'https://example.org:8080');
  var uriObj2 = Uri(
    scheme: 'https',
    host: 'example.org',
    path: '/foo/bar',
    fragment: 'frag',
    queryParameters: {'lang': 'dart'},
  );
  assert(uriObj2.toString() == 'https://example.org/foo/bar?lang=dart#frag');
  var httpUri = Uri.http('example.org', '/foo/bar', {'lang': 'dart'});
  var httpsUri = Uri.https('example.org', '/foo/bar', {'lang': 'dart'});
  assert(httpUri.toString() == 'http://example.org/foo/bar?lang=dart');
  assert(httpsUri.toString() == 'https://example.org/foo/bar?lang=dart');

  // DateTime 示例
  var now = DateTime.now();
  var y2k = DateTime(2000); // January 1, 2000
  var y2k2 = DateTime(2000, 1, 2); // January 2, 2000
  var y2kUtc = DateTime.utc(2000); // 1/1/2000, UTC
  var y2kFromMs = DateTime.fromMillisecondsSinceEpoch(946684800000, isUtc: true);
  var y2kParsed = DateTime.parse('2000-01-01T00:00:00Z');
  var sameTimeLastYear = now.copyWith(year: now.year - 1);
  var y2k3 = DateTime.utc(2000);
  assert(y2k3.millisecondsSinceEpoch == 946684800000);
  var unixEpoch = DateTime.utc(1970);
  assert(unixEpoch.millisecondsSinceEpoch == 0);
  var y2001 = y2k3.add(const Duration(days: 366));
  assert(y2001.year == 2001);
  var december2000 = y2001.subtract(const Duration(days: 30));
  assert(december2000.year == 2000);
  assert(december2000.month == 12);
  var duration = y2001.difference(y2k3);
  assert(duration.inDays == 366); // y2k was a leap year。

  // Line 类和比较
  var short = const Line(1);
  var long = const Line(100);
  assert(short.compareTo(long) < 0);

  // Person 类和相等性
  var p1 = Person('Bob', 'Smith');
  var p2 = Person('Bob', 'Smith');
  var p3 = 'not a person';
  assert(p1.hashCode == p2.hashCode);
  assert(p1 == p2);
  assert(p1 != p3);

  // Process/Processes 示例（不可运行，注释掉）
  // for (final process in Processes()) {
  //   // Do something with the process.
  // }
}

class Line implements Comparable<Line> {
  final int length;
  const Line(this.length);

  @override
  int compareTo(Line other) => length - other.length;
}

class Person {
  final String firstName, lastName;

  Person(this.firstName, this.lastName);

  // Override hashCode using the static hashing methods
  // provided by the `Object` class.
  @override
  int get hashCode => Object.hash(firstName, lastName);

  // You should generally implement operator `==` if you
  // override `hashCode`.
  @override
  bool operator ==(Object other) {
    return other is Person &&
        other.firstName == firstName &&
        other.lastName == lastName;
  }
}

class FooException implements Exception {
  final String? msg;

  const FooException([this.msg]);

  @override
  String toString() => msg ?? 'FooException';
}
// class Process { /* ... */ }
// class ProcessIterator implements Iterator<Process> { /* ... */ }
// class Processes extends IterableBase<Process> { /* ... */ }

